package com.yanqu.road.entity.buildDaoUtil;

import com.yanqu.road.entity.FieldTypeAnnotation;
import com.yanqu.road.entity.activity.goldingotfund.data.GoldIngotFundUserData;
import com.yanqu.road.utils.property.Property;
import com.yanqu.road.utils.string.StringUtils;

import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;

public class BuildUserSql {
    public static void main(String[] args) {
		/*
		PeakSeacraftBattleDetailData
		PeakSeacraftCrossChallengeData
		PeakSeacraftCrossRecordData
		PeakSeacraftCrossUnionData
		PeakSeacraftCrossUnionShipData
		PeakSeacraftCrossUserData
		PeakSeacraftCrossUserGuessData
		PeakSeacraftUserData
		* */
		Class<GoldIngotFundUserData> clz = GoldIngotFundUserData.class;
		String tableName = "";
		String className = clz.getSimpleName();
		Field[] fields = clz.getDeclaredFields();
		boolean isCross = false;
		// 打印SQL=======================================================================
		if (clz.isAnnotationPresent(FieldTypeAnnotation.class)) {
			tableName = "`" + clz.getAnnotation(FieldTypeAnnotation.class).tableName() + "`";
			if (tableName.startsWith("`t_c_")) {
				isCross = true;
			}

			String keyUnique = "";
			List<String> keyList1 = new ArrayList<>();
			String fildStr = "";
			for (Field field : fields) {
				if (field.isAnnotationPresent(FieldTypeAnnotation.class)) {
					FieldTypeAnnotation fieldTypeAnnotation = field.getAnnotation(FieldTypeAnnotation.class);
					String fieldName = "`" + field.getName() + "`";
					String fieldDesc = "'" + fieldTypeAnnotation.desc() + "'";
					if (fieldTypeAnnotation.update()) {
						keyList1.add(fieldName);
						if (keyUnique.length() == 0) {
							keyUnique += "`" + field.getName();
						} else if (keyUnique.length() > 0) {
							keyUnique += "_" + field.getName();
						}
					}

					if (field.getType() == int.class || field.getType() == boolean.class) {
						fildStr += "  " + fieldName + " int(11) NOT NULL COMMENT " + fieldDesc + ",\n";
					} else if (field.getType() == long.class) {
	                    fildStr += "  " + fieldName + " bigint(20) NOT NULL COMMENT " + fieldDesc + ",\n";
	                } else if (field.getType() == Date.class) {
	                    fildStr += "  " + fieldName + " datetime NOT NULL COMMENT " + fieldDesc + ",\n";
	                } else if (field.getType() == BigInteger.class || field.getType() == BigDecimal.class) {
	                    fildStr += "  " + fieldName + " varchar(255) NOT NULL COMMENT " + fieldDesc + ", \n";
	                } else if (field.getType() == String.class ||
	                		field.getType() == List.class || field.getType() == Map.class) {
	                	fildStr += "  " + fieldName + " text NOT NULL COMMENT " + fieldDesc + ", \n";
	                } else {
	                	fildStr += "  " + fieldName + " text NOT NULL COMMENT " + fieldDesc + ", \n";
	                }
	            }
	        }
	        keyUnique += "`";
	        String sql = "CREATE TABLE " + tableName + " (\n" +
	                "  `dbId` bigint(20) NOT NULL AUTO_INCREMENT,\n" +
	                fildStr +
	                "  PRIMARY KEY (`dbId`),\n" +
	                "  UNIQUE KEY " + keyUnique + " (" + StringUtils.listToString(keyList1, ',') + ") USING BTREE\n" +
	                ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;";

	        System.out.println(sql);
	        System.out.print("\n");

	        List<String> selectList = new ArrayList<>();
	        List<String> updateList = new ArrayList<>();
	        List<String> otherList = new ArrayList<>();

	        String buildObject = className + " " + toLowerCaseFirstOne(className) + " = new " + className + "();";
	        buildObject += "\n";

	        String insertHeadSql = "insert into " + tableName + "(";
	        String wenString = ") values (";
	        String callString = "";

	        // update
	        String updateBody = "";
	        String updateBodyTail = "";

	        // insertBody
	        String insertBody = "";

	        String selectParams = "";
	        for (Field field : fields) {
	            if (field.isAnnotationPresent(FieldTypeAnnotation.class)) {
	            	String simpleName = field.getType().getSimpleName();
	            	String fieldName = field.getName();
	                insertHeadSql += fieldName + ",";
	                wenString += "?,";
	                FieldTypeAnnotation fieldTypeAnnotation = field.getAnnotation(FieldTypeAnnotation.class);
	                if (fieldTypeAnnotation.select()) {
	                    selectList.add("`" + field.getName() + "`");
	                    selectParams += simpleName + " " + fieldName + ",";
	                    callString += "call.set" + toUpperCaseFirstOne(simpleName) + "(i++, " + fieldName + ");\n";
	                }
	                if (fieldTypeAnnotation.update()) {
	                    updateList.add(fieldName);
	                }
	                if (!fieldTypeAnnotation.update() && !fieldTypeAnnotation.select()) {
	                    otherList.add(fieldName);
	                }
	                String getFieldString = toLowerCaseFirstOne(className) + ".get" + toUpperCaseFirstOne(fieldName) + "()";
	                String header = toLowerCaseFirstOne(className) + ".set" + toUpperCaseFirstOne(fieldName) + "(";
	                String tail = ");\n";
	                String sqlGetMethodName = "";

	                String updateHeader = "para.put(i++, new DbParameter(Types.";
	                String updateTail = "));\n";
	                String updateParamString = "";
	                if (field.getType() == boolean.class) {
	                    sqlGetMethodName = "rs.getBoolean(\"" + fieldName + "\")";
	                    updateParamString = "BOOLEAN, " + getFieldString;
	                } else if (field.getType() == short.class) {
	                    sqlGetMethodName = "rs.getShort(\"" + fieldName + "\")";
	                    updateParamString = "SMALLINT, " + getFieldString;
	                } else if (field.getType() == int.class) {
	                    sqlGetMethodName = "rs.getInt(\"" + fieldName + "\")";
	                    updateParamString = "INTEGER, " + getFieldString;
	                } else if (field.getType() == long.class) {
	                    sqlGetMethodName = "rs.getLong(\"" + fieldName + "\")";
	                    updateParamString = "BIGINT, " + getFieldString;
	                } else if (field.getType() == String.class) {
	                    sqlGetMethodName = "rs.getString(\"" + fieldName + "\")";
	                    updateParamString = "VARCHAR, " + getFieldString;
	                } else if (field.getType() == Date.class) {
	                    sqlGetMethodName = "rs.getTimestamp(\"" + fieldName + "\")";
	                    updateParamString = "TIMESTAMP, new Timestamp(" + getFieldString + ".getTime())";
	                } else if (field.getType() == BigInteger.class) {
	                    sqlGetMethodName = "new BigInteger(rs.getString(\"" + fieldName + "\"))";
	                    updateParamString = "VARCHAR, " + getFieldString + ".toString()";
	                } else if (field.getType() == BigDecimal.class) {
	                    sqlGetMethodName = "new BigDecimal(rs.getString(\"" + fieldName + "\"))";
	                    updateParamString = "VARCHAR, " + getFieldString + ".toString()";
	                } else if (field.getType() == Property.class) {
	                    sqlGetMethodName = "PropertyUtils.parseStringToProperty(rs.getString(\"" + fieldName + "\"))";
	                    updateParamString = "";
	                } else if (field.getType() == List.class || field.getType() == Map.class) {
	                	String typeName = field.getGenericType().getTypeName();
	                    sqlGetMethodName = "JSON.parseObject(rs.getString(\"" + fieldName + "\"), new TypeReference<" + typeName +">() {})";
	                    updateParamString = "VARCHAR, JSON.toJSONString(" + getFieldString + ")";
	                } else {
	                	// 其它类型统一用JSON字符串处理
	                    sqlGetMethodName = "JSON.parseObject(rs.getString(\"" + fieldName + "\"), " + simpleName +".class)";
	                    updateParamString = "VARCHAR, JSON.toJSONString(" + getFieldString + ")";
	                }
	                buildObject += header + sqlGetMethodName + tail;
	                if (fieldTypeAnnotation.update()) {
	                    updateBodyTail += updateHeader + updateParamString + updateTail;
	                } else {
	                    updateBody += updateHeader + updateParamString + updateTail;
	                }
	                insertBody += updateHeader + updateParamString + updateTail;

	            }
	        }

	        buildObject += "\n";
	        buildObject += "return " + toLowerCaseFirstOne(className) + ";\n";
	        selectParams = selectParams.substring(0, selectParams.length() - 1);
	        insertHeadSql = insertHeadSql.substring(0, insertHeadSql.length() - 1);
	        wenString = wenString.substring(0, wenString.length() - 1) + ")";

	        String insertSql = insertHeadSql + wenString;
	        String selectSql = "select * from " + tableName + getWhereCondition(selectList);
	        String updateSql = "update " + tableName + " set ";
	        for (int i = 0; i < otherList.size(); i++) {
	            updateSql += otherList.get(i) + "=?";
	            if (i + 1 < otherList.size()) {
	                updateSql += ",";
	            }
	        }
	        updateSql += getWhereCondition(updateList);

	        //System.out.println(selectSql);
	        //System.out.println(updateSql);
	        //System.out.println(insertSql);
	        System.out.println("\n");

	        String baseVarName = className + "Sql";
	        String selectVarName = "get" + baseVarName;
	        String updateVarName = "update" + baseVarName;
	        String insertVarName = "insert" + baseVarName;

			System.out.println("public static String " + selectVarName + " = \"" + selectSql + "\";");
			System.out.println("public " + className + " get" + className + "(" + selectParams + ") {\n" +
					"ResultSet rs = null;\n" +
					"CallableStatement call = null;\n" +
					"try {\n" +
					(isCross?"getDb().setConnName(DbNameStrategy.getCrossDb());\n":"getDb().setConnName(DbNameStrategy.getUserDb());\n") +
					"call = getDb().getConnection().prepareCall(" + selectVarName + ");\n" +
					"int i = 1;\n" +
					callString +
					"rs = call.executeQuery();\n" +
					"while (rs.next()) {\n" +
					"return parse(rs);\n" +
					"}\n" +
					"} catch (Exception e) {\n" +
					"throw new RuntimeException(e);" +
					"} finally {\n" +
					"getDb().closeConnection(call, rs);\n" +
					"}\n" +
					"return null;\n" +
					"}\n");


	        System.out.println("public static String " + updateVarName + " = \"" + updateSql + "\";");
	        System.out.println("@Override\nprotected boolean updateData(" + className + " " + toLowerCaseFirstOne(className) + ") {\n" +
	                toLowerCaseFirstOne(className) + ".setBeginWriteTime();\n" +
					(isCross?"getDb().setConnName(DbNameStrategy.getCrossDb());\n":"getDb().setConnName(DbNameStrategy.getUserDb());\n") +
	                "int i = 1;\n" +
	                "Map<Integer, DbParameter> para = new HashMap<Integer, DbParameter>();\n" +
	                updateBody + updateBodyTail +
	                "if (getDb().executeNoneQuery(" + updateVarName + ", para) > 0) {\n" +
	                "return true;\n" +
	                "}\n" +
	                "return false;\n" +
	                "}\n");

	        System.out.println("public static String " + insertVarName + " = \"" + insertSql + "\";");
	        System.out.println("@Override\nprotected boolean addData(" + className + " " + toLowerCaseFirstOne(className) + ") {\n" +
	                toLowerCaseFirstOne(className) + ".setBeginWriteTime();\n" +
					(isCross?"getDb().setConnName(DbNameStrategy.getCrossDb());\n":"getDb().setConnName(DbNameStrategy.getUserDb());\n") +
	                "int i = 1;\n" +
	                "Map<Integer, DbParameter> para = new HashMap<Integer, DbParameter>();\n" +
	                insertBody +
	                "if (getDb().executeNoneQuery(" + insertVarName + ", para) > 0) {\n" +
	                "return true;\n" +
	                "}\n" +
	                "return false;\n" +
	                "\n" +
	                "}\n");

	        System.out.println("@Override\n" +
	                "protected " +className + " parseData(ResultSet rs) throws SQLException {\n" +
	        		buildObject +
	                "}\n");
    	}

        // 打印GET/SET=======================================================================
        String s = "";
        String ss = "";
        String varName = clz.getSimpleName().substring(0, 1).toLowerCase() + clz.getSimpleName().substring(1);
        for (Field field : fields) {
            // 判断是否被标记
        	String simpleName = field.getType().getSimpleName();
            String fieldName = field.getName();
            String fieldNamePart1 = fieldName.substring(0, 1).toUpperCase();
            String fieldNamePart2 = fieldName.substring(1);
            String firstUpperName = fieldNamePart1 + fieldNamePart2;

            if (field.isAnnotationPresent(FieldTypeAnnotation.class)) {
            	if (field.getType() == boolean.class ||
            			field.getType() == short.class ||
            			field.getType() == int.class ||
            			field.getType() == long.class) {
            		s += "	public " + simpleName + " get" + firstUpperName + "() {\n" +
                          "		return " + fieldName + ";\n" +
                          "	}\n";
            		s += "	public void set" + firstUpperName + "(" + simpleName + " " + fieldName + ") {\n" +
                          "		if (this." + fieldName + " != " + fieldName + ") {\n" +
                          "			this." + fieldName + " = " + fieldName + ";\n" +
                          "			setOp(DataStatus.Update);\n" +
                          "		}\n" +
                          "	}\n\n";
            	} else if (field.getType() == String.class ||
            			field.getType() == Date.class ||
            			field.getType() == BigInteger.class ||
            			field.getType() == BigDecimal.class) {
                	s += "	public " + simpleName + " get" + firstUpperName + "() {\n" +
                            "		return " + fieldName + ";\n" +
                            "	}\n";
                    s += "	public void set" + firstUpperName + "(" + simpleName + " " + fieldName + ") {\n" +
                            "		if (" + fieldName + " == null){\n" +
                            "			return;\n" +
                            "		}\n" +
                            "		if (this." + fieldName + " == null || " + "!this." + fieldName + ".equals(" + fieldName + ")) {\n" +
                            "			this." + fieldName + " = " + fieldName + ";\n" +
                            "			setOp(DataStatus.Update);\n" +
                            "		}\n" +
                            "	}\n\n";
            	} else if (field.getType() == List.class ||
            			field.getType() == Map.class) {
            		String typeName = field.getGenericType().getTypeName();
            		s += "	public " + typeName + " get" + firstUpperName + "() {\n" +
                            "		return " + fieldName + ";\n" +
                            "	}\n";
                    s += "	public void set" + firstUpperName + "(" + typeName + " " + fieldName + ") {\n" +
                            "		if (null == " + fieldName + "){\n" +
                            "			return;\n" +
                            "		}\n" +
                            "		if (this." + fieldName + " == null || " + "!this." + fieldName + ".equals(" + fieldName + ")) {\n" +
                            "			this." + fieldName + " = " + fieldName + ";\n" +
                            "			setOp(DataStatus.Update);\n" +
                            "		}\n" +
                            "	}\n\n";
            	} else {
            		// 其它类型统一用JSON字符串处理
            		s += "	public " + simpleName + " get" + firstUpperName + "() {\n" +
                            "		return " + fieldName + ";\n" +
                            "	}\n";
                    s += "	public void set" + firstUpperName + "(" + simpleName + " " + fieldName + ") {\n" +
                            "		if (null == " + fieldName + "){\n" +
                            "			return;\n" +
                            "		}\n" +
                            "		if (this." + fieldName + " == null || " + "!this." + fieldName + ".equals(" + fieldName + ")) {\n" +
                            "			this." + fieldName + " = " + fieldName + ";\n" +
                            "			setOp(DataStatus.Update);\n" +
                            "		}\n" +
                            "	}\n\n";
            	}
                ss += varName + ".set" + firstUpperName + "();\n";
            }
        }
        System.out.println("\n\t// ========================================================================");
        System.out.println(s);

        System.out.println("\n\t// ========================================================================");
        String sNew = clz.getSimpleName() + " " + varName + " = new " + clz.getSimpleName() + "();\n";
        sNew += ss;
        sNew += varName + ".setInsertOption();";
        System.out.println(sNew);
        System.out.println("\n\t// ========================================================================");
    }

    public static String getWhereCondition(List<String> list) {
        String sql = "";
        if (list.size() > 0) {
            for (int i = 0; i < list.size(); i++) {
                if (i == 0) {
                    sql += " where ";
                }
                sql += list.get(i) + "=?";
                if (i + 1 < list.size()) {
                    sql += " and ";
                }
            }
        }
        return sql;
    }

    //首字母转小写
    public static String toLowerCaseFirstOne(String s) {
        if (Character.isLowerCase(s.charAt(0)))
            return s;
        else
            return (new StringBuilder()).append(Character.toLowerCase(s.charAt(0))).append(s.substring(1)).toString();
    }


    //首字母转大写
    public static String toUpperCaseFirstOne(String s) {
        if (Character.isUpperCase(s.charAt(0)))
            return s;
        else
            return (new StringBuilder()).append(Character.toUpperCase(s.charAt(0))).append(s.substring(1)).toString();
    }

}
